if not ClosetGnome then return end

-------------------------------------------------------------------------------
-- Localization                                                              --
-------------------------------------------------------------------------------

local al = LibStub("AceLocale-3.0")
do do local L = al:NewLocale("ClosetGnome-Banker", "enUS", true)
	L["toggle_desc"] = "Toggle whether to always keep this set in your inventory or if it should be put into the bank automatically when visited."
	L["addon_desc"] = "ClosetGnome Banker automatically gets and puts your item sets from and to your bank when you visit it based on the settings below. A checked set will always stay in your inventory, an unchecked one will be flushed to the bank.\n\nIf you want to retrieve a set from the bank, simply equip it while the bank window is open, and it will be moved to your bags.\n\n"
	L["Banker"] = true
	L["flush_and_get"] = "Flush and Get"
	L["bank_full"] = "Your bank is full."
	L["bags_full"] = "Your bags are full."
	L["get_from_bank"] = "Getting '%s' from bank"
	L["done"] = "Done."
	L["aborted"] = "Aborted."
	L["item_not_found"] = "Item not found: %s."
end do local L = al:NewLocale("ClosetGnome-Banker", "ruRU") if L then
	L["Banker"] = "Банкир"
end end do local L = al:NewLocale("ClosetGnome-Banker", "frFR") if L then
	L["Banker"] = "Banquier"
end end do local L = al:NewLocale("ClosetGnome-Banker", "zhCN") if L then
	L["Banker"] = "银行"
end end do local L = al:NewLocale("ClosetGnome-Banker", "koKR") if L then
	L["Banker"] = "은행"
end end do local L = al:NewLocale("ClosetGnome-Banker", "esES") if L then
	L["Banker"] = "Banquero"
end end do local L = al:NewLocale("ClosetGnome-Banker", "zhTW") if L then
	L["Banker"] = "銀行"
end end end

-------------------------------------------------------------------------------
-- Locals                                                                    --
-------------------------------------------------------------------------------

local L = al:GetLocale("ClosetGnome-Banker")
local LBU = LibStub("LibBagUtils-1.0")
local ACR = LibStub("AceConfigRegistry-3.0")


local bankAvailable = nil

-------------------------------------------------------------------------------
-- Addon declaration                                                         --
-------------------------------------------------------------------------------
local addon = CreateFrame("Frame")
addon:SetScript("OnEvent", function(self, event, ...) return self[event](self, ...) end)
_G.CGBanker = addon

-------------------------------------------------------------------------------
-- Initialization                                                            --
-------------------------------------------------------------------------------

local function setDB(info, val)
	local set = info.option.name
	addon.db.char.keep[set] = val and true or nil
end
local function getDB(info)
	local set = info.option.name
	return addon.db.char.keep[set]
end


local options = {
	type = "group",
	name = "Banker",
	set = setDB,
	get = getDB,
	handler = addon,
	args = {
		bankerDescription = {
			type = "description",
			name = L["addon_desc"],
			order = 1,
		},
		flushandget = {
			type="execute",
			name = L["flush_and_get"],
			order = -1,
			disabled = function() return not bankAvailable end,
			func = "FlushAndGet",
		}
	},
}


local sets = {}
local function updateSets()
	for k in pairs(ClosetGnome.db.char.set) do
		table.insert(sets, k)
	end
	table.sort(sets)
	for k, v in pairs(options.args) do
		if k == "bankerDescription" then
			-- nop
		elseif k == "flushandget" then
			-- nop
		else
			v.hidden = true
		end
	end
	local counter = 10
	for i, set in pairs(sets) do
		if options.args[set] then
			options.args[set].hidden = false
			options.args[set .. "_image"].hidden = false
			options.args[set .. "_spacer"].hidden = false
		else
			options.args[set .. "_image"] = {
				type = "description",
				name = "",
				-- image = ... set below
				order = counter,
				width = "half",
			}
			options.args[set] = {
				type = "toggle",
				name = set,
				desc = L["toggle_desc"],
				order = counter+1,
			}
			options.args[set .. "_spacer"] = {
				type = "description",
				name = "",
				order = counter + 2,
			}
		end
		
		options.args[set .. "_image"].order = counter
		options.args[set .. "_image"].image = ClosetGnome.db.char.icons[set] or "Interface\\Icons\\INV_Chest_Cloth_17"
		options.args[set].order = counter + 1
		options.args[set .. "_spacer"].order = counter + 2
		
		counter = counter + 10
	end
	for k in pairs(sets) do sets[k] = nil end
	ACR:NotifyChange("ClosetGnome-Banker")
end

local function init()
	addon.db = LibStub("AceDB-3.0"):New("ClosetGnomeBankerDB", {char = {keep = {}}})

	addon:RegisterEvent("BANKFRAME_OPENED")
	addon:RegisterEvent("BANKFRAME_CLOSED")

	ClosetGnome.RegisterCallback(addon, "AddSet", updateSets)
	ClosetGnome.RegisterCallback(addon, "DeleteSet", updateSets)

	updateSets()

	LibStub("AceConfig-3.0"):RegisterOptionsTable("ClosetGnome-Banker", options)
	LibStub("AceConfigDialog-3.0"):AddToBlizOptions("ClosetGnome-Banker", L["Banker"], "ClosetGnome")
end

ClosetGnome:RegisterCallback("Initialized", init)

-------------------------------------------------------------------------------
-- Meat                                                                      --
-------------------------------------------------------------------------------

function addon:Flush(report)
	if CursorHasItem() or not bankAvailable then return false end

	-- Okay, so, we have to do three things:
	-- 1. Build a list of all the items in all the sets from CG.
	-- 2. Remove the items in the Keep sets from that list.
	-- 3. Move the remaining items from the inventory to the bank.

	-- Step 1.
	local items = {}
	for k, v in pairs(ClosetGnome.db.char.set) do
		for id, item in pairs(v) do
			if type(item)=="string" then
				items[item] = true
			end
		end
	end

	-- Step 2.
	for k in pairs(self.db.char.keep) do
		if not ClosetGnome.db.char.set[k] then
			self.db.char.keep[k] = nil
		else
			for id, item in pairs(ClosetGnome.db.char.set[k]) do
				items[item] = nil
			end
		end
	end
	
	-- Step 3.
	for item in pairs(items) do
		local fromBagId, fromSlotId = LBU:Find("BAGS", item)
		if fromBagId then
			PickupContainerItem(fromBagId, fromSlotId)
			if not LBU:PutItem("BANK") then
				if report then
					print(L["bank_full"])
				end
				return nil -- call me again
			end
		end
	end

	return true -- done
end

do
	local function flushandget(report)
		if CursorHasItem() or not bankAvailable then return false end

		local result = addon:Flush(report)

		for k in pairs(addon.db.char.keep) do
			if addon:RetrieveSet(k,report)==nil then
				return nil
			end
		end
		
		return result
	end

	local total = 0
	local tries = 0
	local function update(self, elapsed)
		total = total + elapsed
		if total > 2 then
			total=0
			tries=tries+1
			local result = flushandget(tries==3)
			
			if result==true then
				print(L["done"])
			elseif result==false or tries>=3 then
				print(L["aborted"])
			else	-- result==nil
				return	-- more!
			end
			self:SetScript("OnUpdate", nil)
		end
	end
	
	
	function addon:FlushAndGet()
		print(L["flush_and_get"].."...")
		for k in pairs(self.db.char.keep) do
			self:IsSetInInventory(k, true)	-- report missing items
		end
		self:SetScript("OnUpdate", update)
		tries=0
		update(addon, 999)
	end

end

function addon:RetrieveSet(setName,report)
	if report==nil then
		report=true
	end
	if CursorHasItem() or type(setName) ~= "string" or not bankAvailable then return false end
	local set = ClosetGnome.db.char.set[setName]
	if not set then return false end

	for _, item in pairs(set) do
		if type(item)=="string" then
			local bagId, slotId = LBU:Find("BANK",item)
			if bagId then
				PickupContainerItem(bagId, slotId)
				if not LBU:PutItem("BAGS") then
					if report then
						print(L["bags_full"])
					end
					return nil	-- call me again
				end
			end
		end
	end
	
	return true -- done
end

-- Checks whether the given set is either in your inventory or is already equipped.
-- If bank is open: don't complain if the items doesn't exist at all
function addon:IsSetInInventory(name, report)
	local set = ClosetGnome.db.char.set[name]
	if not set then return false end

	for _, item in pairs(set) do
		if type(item)=="string" then
			if not (IsEquippedItem(item) or LBU:Find("BAGS",item)) then	-- IsEquippedItem handles inconstants (level) in itemlinks just fine
				if bankAvailable and not LBU:Find("BANK",item) then
					if report then
						print(format(L["item_not_found"], item))
					end
					-- keep looping and pretend like the item was in our inventory
				else
					return false
				end
			end
		end
	end
	return true
end

-------------------------------------------------------------------------------
-- Events                                                                    --
-------------------------------------------------------------------------------

function addon:BANKFRAME_OPENED()
	bankAvailable = true
	ACR:NotifyChange("ClosetGnome-Banker")
end

function addon:BANKFRAME_CLOSED()
	bankAvailable = nil
	ACR:NotifyChange("ClosetGnome-Banker")
end

--------------------------------------------------------------------------------
-- Hooks
--------------------------------------------------------------------------------

do
	local orig = ClosetGnome.WearSet
	ClosetGnome.WearSet = function(self, ...)
		if bankAvailable then
			local set = select(1, ...)
			if not addon:IsSetInInventory(set) then
				print(format(L["get_from_bank"], set))
				addon:RetrieveSet(set)
				return false
			end
		end
		return orig(self, ...)
	end
end



local menuItem = {
	text = L["Banker"],
	func = function()
		local h=200
		for _ in pairs(ClosetGnome.db.char.set) do
			h=h+60
		end
		LibStub("AceConfigDialog-3.0"):SetDefaultSize("ClosetGnome-Banker", 400, min(GetScreenHeight()*0.8, h))
		LibStub("AceConfigDialog-3.0"):Open("ClosetGnome-Banker")
	end
}
CGLDB.RegisterCallback(addon, "OnMenu", function(event, menu)
	tinsert(menu, menuItem)
end)
